package com.zyf.service.audio;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import com.zyf.service.video.VedioUtils;
import com.zyf.utils.FFmpegUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;

import java.io.*;
import java.util.*;

/**
 * 歌词转字幕
 */
public class HandleLrcService {

    /**
     * lrc 转srt
     *
     * @param lrcpath
     * @return
     * @throws FileNotFoundException
     */
    public String handleLrcToSrt(String lrcpath) throws FileNotFoundException {
        StringBuffer buffer = new StringBuffer();
        InputStream inputstream = new FileInputStream(lrcpath);
        BufferedReader reader = null;
        String temp = "";//“” ‘’
        try {
            reader = new BufferedReader(new InputStreamReader(inputstream));
            int index = 0;
            //初始化表头的最后一行
            StringBuffer title = new StringBuffer();
            buffer.append(1);
            buffer.append("\n");
            buffer.append("00:00:00,000");
            buffer.append(" --> ");
            while ((temp = reader.readLine()) != null) {
                index++;
                if (temp.contains("演唱")) {
                    String time = temp.substring(temp.indexOf("[") + 1, temp.indexOf("]")).trim().replace(".", ",");
                    String contend = temp.substring(temp.indexOf("]") + 1).trim();
                    title.append(contend);
                    buffer.append("00:" + time);
                    buffer.append("\n");
                    buffer.append(title);
                    buffer.append("\n");
                    buffer.append("\n");
                    break;
                } else {
                    String contend = temp.substring(temp.indexOf("]") + 1).trim();
                    title.append(contend);
                    title.append("\n");
                }
            }
            List<Map<String, String>> timeList = new ArrayList<>();
            while ((temp = reader.readLine()) != null) {
                Map<String, String> keyContent = new HashMap<>();
                String time = temp.substring(temp.indexOf("[") + 1, temp.indexOf("]")).trim().replace(".", ",");
                String contend = temp.substring(temp.indexOf("]") + 1).trim();
                keyContent.put("00:" + time, contend);
                timeList.add(keyContent);
            }
            int current = 2;
            for (int i = 0; i < timeList.size(); i++) {
                buffer.append(current);
                buffer.append("\n");
                Map<String, String> currentMap = timeList.get(i);
                if (i < timeList.size() - 1) {
                    Map<String, String> nextMap = timeList.get(i + 1);
                    String nextkey = "";
                    for (Map.Entry<String, String> entry : nextMap.entrySet()) {
                        nextkey = entry.getKey();
                    }
                    for (Map.Entry<String, String> entry : currentMap.entrySet()) {
                        String currentkey = entry.getKey();
                        String currentvalue = entry.getValue();
                        buffer.append(currentkey);
                        buffer.append(" --> ");
                        buffer.append(nextkey);
                        buffer.append("\n");
                        buffer.append(currentvalue);
                        buffer.append("\n");
                        buffer.append("\n");
                    }
                } else {
                    StringBuffer timeBuffer = new StringBuffer();
                    for (Map.Entry<String, String> entry : currentMap.entrySet()) {
                        String currentkey = entry.getKey();
                        String currentvalue = entry.getValue();
                        //最后一句歌词加上5秒
                        String[] times = currentkey.split(":");
                        int second = Integer.valueOf(times[1]) + 5;
                        String firstStr = "";
                        String secondStr = "";
                        String lastTime = "";
                        if (second >= 60) {
                            int newfirst = Integer.valueOf(times[0]) + 1;
                            int newsecond = second % 60;
                            if (newfirst < 10) {
                                firstStr = "0" + newfirst;
                            } else {
                                firstStr = "" + newfirst;
                            }
                            if (newsecond < 10) {
                                secondStr = "0" + newsecond;
                            } else {
                                secondStr = "" + newsecond;
                            }
                            lastTime = firstStr + ":" + secondStr + ":" + times[2];
                        } else {
                            if (second < 10) {
                                secondStr = "0" + second;
                            } else {
                                secondStr = "" + second;
                            }
                            lastTime = times[0] + ":" + secondStr + ":" + times[2];
                        }
                        buffer.append(currentkey);
                        buffer.append(" --> ");
                        buffer.append(lastTime);
                        buffer.append("\n");
                        buffer.append(currentvalue);
                        buffer.append("\n");
                        buffer.append("\n");
                    }
                }
                current++;
            }
            //写入文件
            FileWriter writer = null;
            String finalsrt = "";
            try {
                finalsrt = lrcpath.replace(".lrc", "_final.srt");
                writer = new FileWriter(finalsrt, false);
                writer.write(buffer.toString());
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (writer != null) {
                    writer.flush();
                    writer.close();
                }
            }
            return finalsrt;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 统一调度
     *
     * @param vediopath
     * @param type
     * @return
     * @throws Exception
     */
    public boolean mergeVedioAndSrtByType(String vediopath, String type) throws Exception {
        if ("1".equals(type)) {

        }
        if ("2".equals(type)) {
            mergeVedioAndSrt(vediopath);
        }
        if ("3".equals(type)) {
            mergeVedioAndSrtByLight(vediopath);
        }
        return true;
    }

    /**
     * 一键合并歌词和视频
     *
     * @param vediopath
     * @return
     * @throws Exception
     */
    public String barchMergeVedioAndSrt(String vediopath) throws Exception {
        List<File> title = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".srt") && file.getName().contains("title"));
        List<File> srcMp4 = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".mp4"));
        List<File> content = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".srt") && file.getName().contains("content"));
        List<File> mp3List = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".mp3"));

        if (title.isEmpty()) {
            throw new RuntimeException("标题文件不能为空");
        }
        if (srcMp4.isEmpty()) {
            throw new RuntimeException("视频文件不能为空");
        }
        if (content.isEmpty()) {
            throw new RuntimeException("歌词文件不能为空");
        }
        if (mp3List.isEmpty()) {
            throw new RuntimeException("歌曲文件不能为空");
        }
        String titlePath = title.get(0).getAbsolutePath();
        String mp4Path = "";
        String contentPath = content.get(0).getAbsolutePath();
        String mp3Path = mp3List.get(0).getAbsolutePath();
        //判断是否将多个原视频先进行合并再提取
        if(srcMp4.size()>1){
            //合并多个原视频
            mp4Path =  new VedioUtils().batchMergeVedios(vediopath);
        }else{
             mp4Path = srcMp4.get(0).getAbsolutePath();
        }
        //从原视频提取视频
        String fenliMp4 = splitSingleVideo(mp4Path);
        //合并视频和音频
        String mergeMp4 = mergeVediosAndAudio(fenliMp4, mp3Path);
        //合并标题
        String titleMp4 = mergeVedioAndSrt(mergeMp4, titlePath);
        //合并内容
        String finalMp4 = mergeVedioAndSrtByLight(titleMp4, contentPath);

        return finalMp4;
    }

    /**
     * 合并视频和音频,以长度短的为基准
     * <p>
     * ffmpeg -i input_video.mp4 -i input_audio.mp3 -c:v copy -c:a aac -strict experimental -b:a 192k output_video.mp4
     *
     * @throws Exception
     */
    public String mergeVediosAndAudio(String videoPath, String audioPath) throws Exception {
        String videoFinalfile = videoPath.replace(".mp4", "_final.mp4");
        File file = new File(videoFinalfile);
        if (file.exists()) {
            file.delete();
        }
        FFmpegFrameGrabber grabberVideo = null;
        FFmpegFrameGrabber grabberAudio = null;
        try {
            //抓取视频帧
            grabberVideo = new FFmpegFrameGrabber(videoPath);
            //抓取音频帧
            grabberAudio = new FFmpegFrameGrabber(audioPath);
            grabberVideo.start();
            grabberAudio.start();
            //获取时长,以短的文件为主进行截取
            long durationVideo = grabberVideo.getFormatContext().duration() / 1000000;
            long durationAudio = grabberAudio.getFormatContext().duration() / 1000000;
            if (durationVideo != durationAudio) {
                if (durationVideo > durationAudio) {//音频短则以音频长度为主
                    //截取视频
                    videoPath = new VedioUtils().cutVideoByDurationAndStartZ(videoPath, durationAudio);
                } else {
                    //截取音频
                    audioPath = new HandleAudioService().cutAudioByStart(audioPath, durationVideo, "wav");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (grabberVideo != null) {
                grabberVideo.stop();
                grabberVideo.release();
            }
            if (grabberAudio != null) {
                grabberAudio.stop();
                grabberAudio.release();
            }
        }
        try {
            List<String> commands = new ArrayList<>();
            commands.add("ffmpeg");
            commands.add("-i");
            commands.add(videoPath);
            commands.add("-i");
            commands.add(audioPath);
            commands.add("-c:v");
            commands.add("copy");
            commands.add("-c:a");
            commands.add("aac");
            commands.add("-strict");
            commands.add("experimental");
            commands.add("-b:a");
            commands.add("192k");
            commands.add(videoFinalfile);

            String res = FFmpegUtils.execFFmpegCommand(commands);
            System.out.println(res);
        } catch (Exception e) {
            throw new RuntimeException("ffmpeg执行异常" + e.getMessage());
        }
        return videoFinalfile;
    }


    /**
     * 合成视频和字幕
     * // 使用FFmpeg命令行生成视频字幕
     * ffmpeg -i  zimu.mp4   -filter_complex "subtitles=zimu.srt" -c:a copy  zimu_final.mp4
     * <p>
     * 01.Name 风格(Style)的名称. 区分大小写. 不能包含逗号.
     * 02.Fontname        使用的字体名称, 区分大小写.
     * 03.Fontsize        字体的字号
     * 04.PrimaryColour    设置主要颜色, 为蓝-绿-红三色的十六进制代码相排列, BBGGRR. 为字幕填充颜色
     * 05.SecondaryColour  设置次要颜色, 为蓝-绿-红三色的十六进制代码相排列, BBGGRR. 在卡拉OK效果中由次要颜色变为主要颜色.
     * 06.OutlineColour    设置轮廓颜色, 为蓝-绿-红三色的十六进制代码相排列, BBGGRR.
     * 07.BackColour      设置阴影颜色, 为蓝-绿-红三色的十六进制代码相排列, BBGGRR. ASS的这些字段还包含了alpha通道信息. (AABBGGRR), 注ASS的颜色代码要在前面加上&H
     * 08.Bold            -1为粗体, 0为常规
     * 09.Italic          -1为斜体, 0为常规
     * 10.Underline      [-1 或者 0] 下划线
     * 11.Strikeout      [-1 或者 0] 中划线/删除线
     * 12.ScaleX          修改文字的宽度. 为百分数
     * 13.ScaleY          修改文字的高度. 为百分数
     * 14.Spacing        文字间的额外间隙. 为像素数
     * 15.Angle          按Z轴进行旋转的度数, 原点由alignment进行了定义. 可以为小数
     * 16.BorderStyle    1=边框+阴影, 3=纯色背景. 当值为3时, 文字下方为轮廓颜色的背景, 最下方为阴影颜色背景.
     * 17.Outline        当BorderStyle为1时, 该值定义文字轮廓宽度, 为像素数, 常见有0, 1, 2, 3, 4.
     * 18.Shadow          当BorderStyle为1时, 该值定义阴影的深度, 为像素数, 常见有0, 1, 2, 3, 4.
     * 19.Alignment      定义字幕的位置. 字幕在下方时, 1=左对齐, 2=居中, 3=右对齐. 1, 2, 3加上4后字幕出现在屏幕上方. 1, 2, 3加上8后字幕出现在屏幕中间. 例: 11=屏幕中间右对齐. Alignment对于ASS字幕而言, 字幕的位置与小键盘数字对应的位置相同.
     * 左 中 右
     * 上  5  6  7
     * 中  9 10 11
     * 下  1  2  3
     * <p>
     * 20.MarginL        字幕可出现区域与左边缘的距离, 为像素数
     * 21.MarginR        字幕可出现区域与右边缘的距离, 为像素数
     * 22.MarginV        垂直距离
     *
     * @return
     * @throws Exception
     */
    public boolean mergeVedioAndSrt(String vediopath) throws Exception {
        List<File> srtList = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".srt"));
        List<File> mp4List = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".mp4"));
        String videofile = mp4List.get(0).getAbsolutePath();
        String subtitleFile = srtList.get(0).getAbsolutePath();
        subtitleFile = subtitleFile.replace("\\", "\\\\").replace(":", "\\:");
        String videoFinalfile = videofile.replace(".mp4", "_final.mp4");
        File file = new File(videoFinalfile);
        if (file.exists()) {
            file.delete();
        }
        //获取视频的宽高
        int[] videoInfo=getVideoHeight( videofile);
        try {
            List<String> commands = new ArrayList<>();
            commands.add("ffmpeg");
            commands.add("-i");
            commands.add(videofile);
            commands.add("-filter_complex");
            commands.add("\"subtitles='" + subtitleFile + "':force_style='Alignment=2,MarginV="+videoInfo[1]/2+",Fontsize=20,Bold=-1,Fontname=宋体,fontcolor=white'\"");
            commands.add("-c:a");
            commands.add("copy");
            commands.add(videoFinalfile);

            String res = FFmpegUtils.execFFmpegCommand(commands);
            System.out.println(res);
        } catch (Exception e) {
            throw new RuntimeException("ffmpeg执行异常" + e.getMessage());
        }
        return true;
    }

    /**
     * 具体指向两个文件
     *
     * @return
     * @throws Exception
     */
    public String mergeVedioAndSrt(String videofile, String subtitleFile) throws Exception {
        subtitleFile = subtitleFile.replace("\\", "\\\\").replace(":", "\\:");
        String videoFinalfile = videofile.replace(".mp4", "_final.mp4");
        File file = new File(videoFinalfile);
        if (file.exists()) {
            file.delete();
        }
        //获取视频的宽高
        int[] videoInfo=getVideoHeight( videofile);
        try {
            List<String> commands = new ArrayList<>();
            commands.add("ffmpeg");
            commands.add("-i");
            commands.add(videofile);
            commands.add("-filter_complex");
            commands.add("\"subtitles='" + subtitleFile + "':force_style='Alignment=2,MarginV="+videoInfo[1]/2+",Fontsize=20,Bold=-1,Fontname=宋体,fontcolor=white'\"");
            commands.add("-c:a");
            commands.add("copy");
            commands.add(videoFinalfile);

            String res = FFmpegUtils.execFFmpegCommand(commands);
            System.out.println(res);
        } catch (Exception e) {
            throw new RuntimeException("ffmpeg执行异常" + e.getMessage());
        }
        return videoFinalfile;
    }

    public boolean mergeVedioAndSrtByLight(String vediopath) throws Exception {
        List<File> srtList = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".srt"));
        List<File> mp4List = FileUtil.loopFiles(vediopath, file -> file.isFile() && file.getName().endsWith(".mp4"));
        String videofile = mp4List.get(0).getAbsolutePath();
        String subtitleFile = srtList.get(0).getAbsolutePath();
        subtitleFile = subtitleFile.replace("\\", "\\\\").replace(":", "\\:");
        String videoFinalfile = videofile.replace(".mp4", "_final.mp4");
        File file = new File(videoFinalfile);
        if (file.exists()) {
            file.delete();
        }
        try {
            List<String> commands = new ArrayList<>();
            commands.add("ffmpeg");
            commands.add("-i");
            commands.add(videofile);
            commands.add("-filter_complex");
            commands.add("\"subtitles='" + subtitleFile + "':force_style='Alignment=11,MarginV=60,Fontsize=20,Bold=-1,Fontname=宋体,fontcolor=white'\"");
            commands.add("-c:a");
            commands.add("copy");
            commands.add(videoFinalfile);

            String res = FFmpegUtils.execFFmpegCommand(commands);
            System.out.println(res);
        } catch (Exception e) {
            throw new RuntimeException("ffmpeg执行异常" + e.getMessage());
        }
        return true;
    }

    public String mergeVedioAndSrtByLight(String videofile, String subtitleFile) throws Exception {
        subtitleFile = subtitleFile.replace("\\", "\\\\").replace(":", "\\:");
        String videoFinalfile = videofile.replace(".mp4", "_final.mp4");
        File file = new File(videoFinalfile);
        if (file.exists()) {
            file.delete();
        }
        try {
            List<String> commands = new ArrayList<>();
            commands.add("ffmpeg");
            commands.add("-i");
            commands.add(videofile);
            commands.add("-filter_complex");
            commands.add("\"subtitles='" + subtitleFile + "':force_style='Alignment=11,MarginV=60,Fontsize=20,Bold=-1,Fontname=宋体,fontcolor=white'\"");
            commands.add("-c:a");
            commands.add("copy");
            commands.add(videoFinalfile);

            String res = FFmpegUtils.execFFmpegCommand(commands);
            System.out.println(res);
        } catch (Exception e) {
            throw new RuntimeException("ffmpeg执行异常" + e.getMessage());
        }
        return videoFinalfile;
    }

    /**
     * 获取视频的宽和高
     * @param videoPath
     * @return
     * @throws Exception
     */
    public int[] getVideoHeight(String videoPath) throws Exception {
        FFmpegFrameGrabber grabberVideo = null;
        try {
            //抓取视频帧
            grabberVideo = new FFmpegFrameGrabber(videoPath);
            //抓取音频帧
            grabberVideo.start();
            //获取时长,以短的文件为主进行截取
            int  height = grabberVideo.getImageHeight();
            int width = grabberVideo.getImageWidth();
            int[] result={width,height};
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (grabberVideo != null) {
                grabberVideo.stop();
                grabberVideo.release();
            }

        }
        return null;
    }

    /**
     * 分离出视频
     *
     * @param videoFolderpath
     * @throws Exception
     */
    public String splitSingleVideo(String videoFolderpath) throws Exception {
        String outfilepath = videoFolderpath.replace(".mp4", "_fenli.mp4");

        List<String> commands = new ArrayList<>();
        commands.add("ffmpeg");
        commands.add("-i");
        commands.add(videoFolderpath);
        commands.add("-vcodec");
        commands.add("copy");
        commands.add("-an");
        commands.add(outfilepath);

        String result = FFmpegUtils.execFFmpegCommand(commands);
        System.out.println(result);

        return outfilepath;

        // 音频、视频都保留原格式。 这种方式转换最快。
        // ffmpeg -i test.mp4 -acodec copy -vcodec copy -an video.mp4
//这里也可以加上 -ss 或者 -t 参数只保留视频的一部分。 视频裁剪用的就是这两个参数
// 只保留音频。 转换后只有音频没有视频
        //   ffmpeg -i test.mp4 -acodec copy -vn audio.mp4
// 只保留视频。 转换后只有视频没有音频
        //    ffmpeg -i test.mp4 -vcodec copy -an video.mp4


// 保留编码格式
        // ffmpeg -i test.mp4 -vcodec copy -an test_copy.h264


// 强制格式
        //  ffmpeg -i test.mp4 -vcodec libx264 -an test.h264
        //  ffmpeg -i test.mp4 -vcodec flv -an test.flv


//保留编码格式
        //    ffmpeg -i test.mp4 -acodec copy -vn test.aac

//强制格式
        //   ffmpeg -i test.mp4 -acodec libmp3lame -vn test.mp3
        //   ffmpeg -i test.mp4 -acodec pcm_s16le -vn test.wav


    }


}
